package org.javaboy.circuitbreaker.service;

import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import io.github.resilience4j.ratelimiter.RateLimiterRegistry;
import io.github.resilience4j.ratelimiter.annotation.RateLimiter;
import io.github.resilience4j.retry.RetryConfig;
import io.github.resilience4j.retry.RetryRegistry;
import io.github.resilience4j.retry.annotation.Retry;
import org.javaboy.commons.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.util.Date;

@Service
public class UserServiceImpl {
    @Autowired
    IUserService userService;

    @Autowired
    CircuitBreakerFactory cbFactory;

    @Autowired
    RateLimiterRegistry rateLimiterRegistry;

    @Autowired
    RetryRegistry retryRegistry;

    /**
     * fallbackMethod 就是备用方法（服务降级方法）
     * 当前方法执行出错的时候，会调用 fallbackMethod 指定的方法
     * 注意，fallbackMethod 指定的方法必须是当前类的实例方法，并且参数和返回值类型必须和当前方法一致
     * 参数必须一致，但是需要注意，参数顺序必须和当前方法一致，不能是其他顺序
     * fallback 方法多了一个 Throwable 参数，这个参数必须有
     */
//    @CircuitBreaker(name = "getUserById", fallbackMethod = "getUserByIdFallback")
    @Retry(name = "provider", fallbackMethod = "getUserByIdFallback")
    public User getUserById() {
        System.out.println("getUserById>>>" + new Date());
        return userService.getUserById(1, "token");
    }

    public void getUserById4() {
        retryRegistry.retry("xxx", new RetryConfig.Builder<>()
                .maxAttempts(5)
                .waitDuration(Duration.ofSeconds(2))
                //忽略的异常，即哪些异常不需要重试
                .ignoreExceptions(ArithmeticException.class)
                .build()).executeRunnable(() -> {
            System.out.println("getUserById4:" + new Date());
//            userService.getUserById(1, "token");
            int i = 1 / 0;
        });
    }

    /**
     * @return
     * @RateLimiter 限制的是当前方法执行频率
     */
    @RateLimiter(name = "provider")
    public User getUserById2() {
        return cbFactory.create("mycb")
                .run(() -> userService.getUserById(1, "token"), throwable -> new User(1, "fallback"));
    }

    public void getUserById3() {
        rateLimiterRegistry.rateLimiter("provider").executeRunnable(() -> {
            System.out.println("getUserById3:" + new Date());
        });
    }

    public User getUserByIdFallback(Throwable throwable) {
        System.out.println("getUserByIdFallback");
        return new User(1, "fallback");
    }

}
